Snipe.Commands = new Class({

    Implements: [Events, Options],

    options: {
        //onAction:function(command,value){ .. },
        cmds: "data-cmd" //toolbar button data attribute
        //relativeTo: document.body //default position of a dialog
        //dialogs:{ cmd1:dialog1, cmd2:dialog2, ...}
    },
    dlgs: {},  //all cmd:instantiated dialogs  (lazy activation)
    btns: {},  //all button DOM elements
    dialogs: {},  //all cmd:dialogs  definitions

    initialize: function( container, options ) {

    	var self = this.setOptions(options);
        var dataCmd = self.options.cmds;
        var command;
        var dialog;
        var dialogs = options.dialogs || {};

        //add click buttons and dialogs
        container.addEvent("click:relay([" + dataCmd + "])", function(event) {
        	var cmd = this.get( dataCmd );
        	var dlg = self.dlgs[ cmd ];

        	dlg ? dlg.toggle() : self.action( cmd );

        	// input fields (eg checkboxes) keep the default behaviour; other click events are disabled
        	if( !this.match("input") ) { event.stop(); }
       	});

        //see if there are any dialogs linked to a button. Eg: "div.dialog.<command>"
        container.getElements("[" + dataCmd + "]").each( function(button) {

            command = button.get(dataCmd);
            self.btns[command] = button;

            if( (dialog = container.getElement(".dialog." + command)) ) {
                if( !dialogs[command] ) {
                	dialogs[command] = [Dialog, {}];
                }
                options = dialogs[command][1];
                //register the DOM dialog element, and move to top of DOM for proper absolute positioning
                options.dialog = dialog.inject(document.body);
                options.relativeTo = button;  //register the dialog positioning info
            }
        });

        self.addDialogs( dialogs );
    }, // -- END -- initialize: function( container, options )

    /*
    Function: addDialog
        Add a new dialogs.

    Arguments:
        newdialogs: {cmd:[Dialog-Class, {options}]..}
    */
    addDialogs: function( newdialogs ){

        var dialog,
            command,
            dialogs = this.dialogs;

        for( command in newdialogs ){

            if( dialogs[command] ){
                console.log("Snipe.Commands addDialogs() - warning: double registration of => " + command);
            }

            dialog = dialogs[command] = newdialogs[command];

            //note: make sure to initialize this.dialogs[command] prior to calling show()
            if( instanceOf( dialog, Dialog ) ){ this.attach(dialog, command); }

        }
        //console.log("allDialogs: " + Object.keys(this.dialogs) );
    },

    /*
    Function: attach
        Attach event-handlers to a dialog
    */
    attach: function(dialog, command){

        var self = this,
            //fire ACTION event back to the invoker of the Snipe.Commands
            actionHdl = function(value) { self.fireEvent("action", [command, value]); };

        //console.log("Snipe.Commands: attachDialog() ", command, dialog);

        return self.dlgs[command] = dialog.addEvents({
            open: self.openDialog.bind(self, command),
            close: self.closeDialog.bind(self, command),
            action: actionHdl,
            drag: actionHdl
        });
    },

    /*
    Function: action
        Action handler for a simple command.
        Send the "action" event back to the Snipe Editor.

    Arguments:
        command : command name
        value : (optional) initial value of the dialog
    */
    action: function( command, value ) {

        var self = this;
        var active = "active";
        var button = self.btns[command];
        var dialog;

        //console.log("Commands.action ", command, " value:", value, " btn=", button, " dlg=", dialog);
        //if( button ) button = document.id( button);

        if( button && button.match(".disabled") ) {
            //nothing to be done here
        } else if( self.dialogs[command] ) {
            dialog = self.dlgs[command] || self.createDialog(command);
            if( value != null ) {
            	dialog.setValue( value );
            }
            dialog.show();
        } else {
            if( button ) {
            	button.addClass(active);
            }
            self.fireEvent("action", [command, value] );
            if( button ) {
            	button.removeClass(active);
            }
        }
    },

    /*
    Function: createDialog
        Create a new dialog, based on dialog creation parameters in this.dlgs :
        - [ Dialog-class, { options } ]
        - otherwise convert to Dialog.Selection dialog


    Arguments
        command - (string) command

        The open/close handlers will make sure only one dialog is open at the
        same time. The open dialog is stored in {{this.activecommand}}.

        The key events "action", "drag", "open" and "close" are propagated upwards.

    Returns:
        The created dialog, which is also stored in this.dlgs[] repository.

    */
    createDialog: function( command ) {

        var dialog = Function.from( this.dialogs[command] )();

        //console.log("Snipe.Commands: createDialog() " + command + " ",dialog );

        if( typeOf(dialog) != "array" ) {
        	dialog = [ Dialog.Selection, { body: dialog } ];
        }

        if( !dialog[1].relativeTo ) {
        	dialog[1].relativeTo = this.options.relativeTo || document.body;
        }

        dialog[1].autoClose = false;

        //note: make sure to initialize this.dialogs[command] prior to calling show()
        return this.attach( new dialog[0]( dialog[1] ), command);
    },

    /*
    Function: openDialog
        Opens a dialog. If already another dialog was open, that one will first be closed.
        When a toolbar button exists, it will get the css class ".active".

        Note: make sure that this.dlgs[cmd] is initialized prior to calling show() !

    Argument:
        command - dialog to be opened
    */
    openDialog: function(command) {

        var self = this;
        var activeDlg = self.activeDlg;
        var newDlg = self.dlgs[command];
        var button = self.btns[command];

        //console.log("Snipe.Commands: openDialog() " + command + " " + activeDlg);

        if( activeDlg && (activeDlg != newDlg) ) {
        	activeDlg.hide();
        }
        self.activeDlg = self.dlgs[command];

        if( button ) {
        	button.addClass("active");
        }

        self.fireEvent("open", command);
    },

    /*
    Function: closeDialog

    Arguments:
        command - (mandatory) dialog to be closed
    */
    closeDialog: function(command /*, dialog*/) {

    	var self = this;
        var button = self.btns[command];

        //console.log("Snipe.Commands: closeDialog() " + command )

        if( self.dlgs[command] == self.activeDlg ) {
        	self.activeDlg = null;
        }
        if( button ) {
        	button.removeClass("active");
        }

        self.fireEvent("close", command);
    },

    /*
    Function: close
        Close any active dialog.
    */
    close: function() {
        var activeDlg = this.activeDlg;
        if( activeDlg ) {
        	activeDlg.hide();
        }
    }

});